<template>
  <div class="uploader">
    <a-space>
      <a-upload :show-upload-list="false" :accept="accepts.mime" :file-list="fileList" :before-upload="beforeUpload">
        <a-button :disabled="disabled"><a-icon type="upload" />选择文件</a-button>
      </a-upload>
      <a-button type="primary" :loading="uploading" :disabled="finished" @click.stop.prevent="handleUpload">
        开始上传
      </a-button>
    </a-space>
    <div
      ref="uploaderListRef"
      v-if="fileList && fileList.length"
      class="uploader-list"
      :style="{ margin: gutter + 'px 0 0', overflow: 'hidden' }"
    >
      <a-row :style="{ margin: -1 * (gutter / 2) + 'px' }">
        <a-col
          v-for="(item, index) of fileList"
          :key="item.uid"
          :span="colSpan"
          :style="{ padding: gutter / 2 + 'px' }"
        >
          <div class="uploader-list-item" :class="`uploader-list-item-${item.status}`">
            <div class="uploader-list-item-del" @click="handleRemove(index)">×</div>
            <div class="uploader-list-item-preview" @click.stop.prevent="handlePreview(item)">
              <div
                v-if="item && item.url && isImage(item)"
                :style="{
                  width: size + 'px',
                  height: size + 'px',
                  background: `url(${item.url}) center center / cover no-repeat`,
                }"
              >
                <img
                  :src="item.url"
                  alt="preload"
                  style="display: none; width: 0; height: 0; visibility: hidden; opacity: 0"
                />
              </div>
              <FileOutlined v-else :style="{ fontSize: size + 'px' }" />
            </div>
            <div class="uploader-list-item-label" :title="item.name">{{ item.name }}</div>
          </div>
        </a-col>
      </a-row>
    </div>
  </div>
</template>

<script>
import { get, isEqual } from 'lodash-es';
import { Modal } from 'ant-design-vue';
import { FileOutlined } from '@ant-design/icons-vue';
import { uploadFiles } from '../../api/index.js';
import * as uploadUtils from './upload-utils.js';

export default {
  name: 'Uploader',
  components: {
    FileOutlined,
  },
  props: {
    /* 对象数组 [ { uid,url,name,type,suffix,status,... },... ] */
    modelValue: { type: Array, default: undefined },
    /* url 数组或字符串 */
    urls: { type: [String, Array], default: undefined },
    /* name 数组或字符串 */
    names: { type: [String, Array], default: undefined },
    valueType: { type: [String], default: 'string' },
    accept: { type: [String, Array], default: undefined },
    filePath: { type: String, default: '/attachment' },
    maxCount: { type: Number, default: 1 },
    gutter: { type: Number, default: 8 },
    colSpan: { type: Number, default: 6 },
    size: { type: Number, default: 60 },
  },
  emits: ['update:modelValue', 'update:urls', 'update:names', 'change'],
  data() {
    return {
      uploading: false,
      finished: false,
      fileList: [],
      fileNames: [],
    };
  },
  computed: {
    disabled() {
      return this.fileList.length >= this.maxCount;
    },
    accepts() {
      return uploadUtils.getAcceptTypes(this.accept, { useDefault: false });
    },
  },
  mounted() {
    this.$watch(
      () => this.urls,
      (values) => {
        values = this.getInputValues(values);
        let urls = this.getResponseData();
        if (!isEqual(urls, values)) {
          let names = this.getInputValues(this.names);
          this.fileList = uploadUtils.getFileList(values, names);
        }
      },
      { immediate: true }
    );
    this.$watch(
      () => this.modelValue,
      (values) => {
        if (Array.isArray(values) && !isEqual(values, this.fileList)) {
          this.fileList = values;
        }
      },
      { immediate: true }
    );
  },
  methods: {
    isImage: uploadUtils.isImage,
    getInputValues(values) {
      if (typeof values === 'string') {
        values = values.split(',');
      }
      if (Array.isArray(values)) {
        return values;
      }
      return [];
    },
    getResponseData(keyPath) {
      if (!keyPath) {
        keyPath = 'response.data.url';
      }
      return this.fileList.map((item) => get(item, keyPath)).filter(Boolean);
    },
    beforeUpload(file) {
      if (!uploadUtils.isFileAcceptable(file, this.accept)) {
        uploadUtils.handleNotice(this.accept);
        return false;
      }
      file = uploadUtils.getWrappedFile(file, { status: 'waiting' });
      this.fileList = [...this.fileList, file].slice(-1 * Number(this.maxCount));
      return false;
    },
    handleRemove(index) {
      this.fileList.splice(index, 1);
      this.output();
    },
    handlePreview(item) {
      uploadUtils.handlePreview(item);
    },
    async handleUpload() {
      const fileList = this.fileList.filter((item) => item.status !== 'done');
      if (!fileList.length) {
        return this.$message.error('没有需要上传的文件,请先选择文件!');
      }
      this.uploading = true;
      await Promise.all(
        fileList.map(async (file) => {
          const formData = new FormData();
          formData.append('file', file.originFileObj);
          formData.append('filePath', this.filePath);
          const res = await uploadFiles(formData).catch(() => {
            file.status = 'fail';
          });
          if (res && res.code === 200) {
            file.status = 'done';
            file.response = {
              data: { url: get(res, 'data.url'), filename: file.name },
            };
          } else {
            file.status = 'fail';
          }
        })
      ).catch((error) => {
        console.error('upload failed: ', error);
        this.$message.error('文件上传失败!');
        this.uploading = false;
      });
      this.fileList = [...this.fileList];
      this.$message.success('文件上传成功!');
      this.uploading = false;
      this.output();
    },
    async checkUploads() {
      return await new Promise((resolve) => {
        const fileList = this.fileList.filter((item) => item.status !== 'done');
        if (!fileList.length) {
          return resolve();
        }
        Modal.confirm({
          title: '当前有未上传的文件，是否立即上传？',
          onOk: async () => {
            await this.handleUpload();
            resolve();
          },
          onCancel: () => resolve(),
        });
      });
    },
    output() {
      let urls = this.getResponseData('response.data.url');
      let names = this.getResponseData('response.data.filename');
      if (this.valueType === 'string') {
        urls = urls.join(',');
        names = names.join(',');
      }
      // console.log(this.fileList);
      // console.log(urls);
      this.$emit('update:urls', urls);
      this.$emit('update:names', names);
      this.$emit('update:modelValue', this.fileList);
      this.$emit('change', this.getResponseData('response.data'));
    },
  },
};
</script>

<style scoped lang="less">
.uploader {
  .uploader-list {
    .uploader-list-item {
      position: relative;
      padding: 10px;
      border: 2px solid #d9d9d9;
      border-radius: 4px;
      color: #a0a0a0;

      &.uploader-list-item-waiting {
        border-color: #d9d9d9;
      }
      &.uploader-list-item-done {
        border-color: #54d016;
      }
      &.uploader-list-item-fail {
        border-color: #ff4d4f;
      }
      .uploader-list-item-del {
        position: absolute;
        top: 0;
        right: 0;
        font-size: 18px;
        line-height: 0.5;
        cursor: pointer;
      }
      .uploader-list-item-preview {
        position: relative;
        display: flex;
        justify-content: center;
        align-items: center;
        width: 100%;
      }
      .uploader-list-item-label {
        line-height: 1.1;
        overflow: hidden;
        white-space: nowrap;
        text-overflow: ellipsis;
        text-align: center;
      }
    }
  }
}
</style>
